<?php
declare(strict_types=1);

namespace App\Utils\Common\SumpayRsa {

    use App\Exception\InternalException;
    use Hyperf\Collection\Arr;
    use function App\Utils\Common\Aes\encrypt as aesEncrypt;
    use function Hyperf\Translation\__;

    /**
     * 生成随机字符串
     * @param int $size
     * @return string
     * @throws \Exception
     */
    function generate(int $size=32): string
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $length = strlen($str);
        $data = [];
        if (function_exists('random_int')) {
            for ($i = 0; $i < $size; $i++) {
                $data[] = $str[random_int(0, $length - 1)];
            }
        } else {
            for ($i = 0; $i < $size; $i++) {
                mt_srand(crc32(uniqid(). microtime()));
                $data[] = $str[mt_rand(0, $length - 1)];
            }
        }

        return implode('', $data);
    }

    /**
     * 处理json数组字段
     * @param array $data
     * @param array $encryptFields
     * @param array $jsonArrayFields
     * @param array $specialFields
     * @return array
     * @throws \Exception
     */
    function processJsonArrayFields(array $data, string $aesKey, array $encryptFields = [], array $jsonArrayFields = [], array $specialFields = []): array
    {
        foreach ($jsonArrayFields as $field) {
            if (Arr::has($data, $field)) {
                $jsonArray = json_decode(Arr::get($data, $field), true);
                for ($i = 0; $i < count($jsonArray); $i++) {
                    $jsonObject = $jsonArray[$i];
                    $jsonObject = charsetChange($jsonObject);
                    $jsonObject = encryptByAesKey($jsonObject, $aesKey, $encryptFields);
                    $jsonObject = specialChange($jsonObject, $specialFields);
                    $jsonArray[$i] = $jsonObject;
                }
                $data[$field] = json_encode($jsonArray, JSON_UNESCAPED_UNICODE);
            }
        }

        return $data;
    }

    function charsetChange(array $data): array
    {
//        foreach ($data as $key => $value) {
//            if (is_string($value)) {
//                $code = mb_detect_encoding($value, mb_detect_order(), true);
//                if ($code !== 'UTF-8') {
//                    $data[$key] = iconv($code, 'UTF-8', $value);
//                }
//            }
//        }

        return $data;
    }

    /**
     * 根据AesKey加密
     * @param array $data
     * @param array $encryptFields
     * @return array
     * @throws \Exception
     */
    function encryptByAesKey(array $data, string $aesKey, array $encryptFields = []): array
    {
        foreach ($encryptFields as $field) {
            if (Arr::get($data, $field)) {
                $data[$field] = base64_encode(
                    aesEncrypt(
                        $aesKey, $data[$field], 'AES-256-ECB', OPENSSL_RAW_DATA | PKCS7_NOATTR
                    )
                );
            }
        }

        return $data;
    }

    /**
     * base64加密特殊字段
     * @param array $data
     * @param array $specialFields
     * @return array
     */
    function specialChange(array $data, array $specialFields=[]): array
    {
        foreach ($specialFields as $field) {
            if (Arr::get($data, $field)) {
                $data[$field] = base64_encode($data[$field]);
            }
        }

        return $data;
    }

    /**
     * 加密数据
     * @param string $data
     * @param string $publicKeyFile
     * @return string
     */
    function encrypt(string $data, string $publicKeyFile): string
    {
        openssl_public_encrypt($data, $encryptedData, file_get_contents($publicKeyFile));
        return base64_encode($encryptedData);
    }

    /**
     * 验证签名
     * @param string $data
     * @param string $sign
     * @param string $publicKeyFile
     * @return bool
     */
    function verify(string $data, string $sign, string $publicKeyFile): bool
    {
        $result = openssl_verify($data, base64_decode($sign), file_get_contents($publicKeyFile), 'sha256');
        if ($result !== 1) {
            throw new InternalException(__('message.sign.failed'));
        }

        return true;
    }

    /**
     * 生成签名
     * @param string $data
     * @param string $privateKeyFile
     * @param $password
     * @return string
     */
    function sign(string $data, string $privateKeyFile, string $password): string
    {
        $certs = [];
        openssl_pkcs12_read(file_get_contents($privateKeyFile), $certs, $password);
        if (!$certs) {
            throw new InternalException(__('message.config.private_cert'));
        }
        $sign = '';
        openssl_sign($data, $sign, $certs['pkey'], 'sha256');
        return base64_encode($sign);
    }

    /**
     * 拼接数据
     * @param array $data
     * @return string
     */
    function splic(array $data): string
    {
        $data = array_filter($data, fn ($v) => $v || is_numeric($v));
        $data = array_map(fn ($v) => is_string($v) ? htmlspecialchars_decode($v) : $v, $data);
        ksort($data);
        return urldecode(http_build_query($data));
    }
}